package com.digua.base.socket

import com.digua.base.socket.dispatcher.MainThreadResponseDelivery
import com.digua.base.socket.dispatcher.ResponseDelivery
import com.digua.base.socket.dispatcher.ResponseProcessEngine
import com.digua.base.socket.request.Request
import com.digua.base.socket.request.RequestFactory
import com.digua.base.socket.response.ErrorResponse
import com.digua.base.socket.response.Response
import com.digua.base.socket.response.ResponseFactory
import com.digua.base.util.LogUtil
import org.java_websocket.framing.Framedata
import org.java_websocket.framing.PingFrame
import java.nio.ByteBuffer


class WebSocketManager {
    var setting: WebSocketSetting? = null
        private set
    private var webSocket: WebSocketWrapper? = null

    //注册的监听器集合
    private var delivery: ResponseDelivery? = null
    private var reconnectManager: ReconnectManager? = null
    private var socketWrapperListener: SocketWrapperListener

    //当前是否已销毁
    private var destroyed = false

    //用户调用了 disconnect 方法后为 true
    private var disconnect = false
    private var webSocketEngine: WebSocketEngine? = null
    private var responseProcessEngine: ResponseProcessEngine? = null

    constructor(setting: WebSocketSetting?, webSocketEngine: WebSocketEngine?, responseProcessEngine: ResponseProcessEngine?) {
        this.setting = setting
        this.webSocketEngine = webSocketEngine
        this.responseProcessEngine = responseProcessEngine
        delivery = this.setting?.responseDelivery
        if (delivery == null) {
            delivery = MainThreadResponseDelivery()
        }
        socketWrapperListener = getSocketWrapperListener()
        if (webSocket == null) {
            webSocket = WebSocketWrapper(this.setting, socketWrapperListener)
        }
        start()
    }

    /**
     * WebSocket 是否已链接
     */
    fun isConnected() = webSocket?.let { it.getConnectState() == 2 || reconnectManager?.isConnected() == true } ?: false

    /**
     * WebSocket 是否链接中
     */
    fun isConnectIng() = webSocket?.let { it.getConnectState() == 1 || reconnectManager?.isReconnecting() == true } ?: false

    /**
     * WebSocket 是否未链接
     */
    fun isNoConnect() = webSocket?.let { it.getConnectState() == 0 } ?: true

    /**
     * WebSocket 链接状态
     */
    fun connectState() = webSocket?.getConnectState()

    /**
     * 设置重连管理类。
     * 用户可根据需求设置自己的重连管理类，只需要实现接口即可
     */
    fun setReconnectManager(reconnectManager: ReconnectManager?) {
        this.reconnectManager = reconnectManager
    }

    /**
     * 启动，调用此方法开始链接
     */
    fun start(): WebSocketManager {
        if (webSocket == null) webSocket = WebSocketWrapper(setting, socketWrapperListener)
        if (isNoConnect()) reconnect()
        return this
    }

    /**
     * 通过 [ReconnectManager] 开始重接
     */
    fun reconnect(): WebSocketManager {
        if (webSocket == null) return start()
        disconnect = false
        if (reconnectManager == null) {
            reconnectManager = getDefaultReconnectManager()
        }
        reconnectManager?.apply { if (!reconnecting()) startReconnect() }
        return this
    }

    /**
     * 使用新的 Setting 重新创建链接，同时会销毁之前的链接
     */
    fun reconnect(setting: WebSocketSetting?): WebSocketManager {
        disconnect = false
        if (destroyed) {
            LogUtil.e("This WebSocketManager is destroyed!")
            return this
        }
        this.setting = setting
        webSocket?.destroy()
        webSocket = null
        start()
        return this
    }

    /**
     * 断开链接，断开后可使用 [this.reconnect] 方法重新建立链接
     */
    fun disConnect(): WebSocketManager {
        disconnect = true
        if (destroyed) {
            LogUtil.e("This WebSocketManager is destroyed!")
            return this
        }
        if (webSocket?.getConnectState() != 0) {
            webSocketEngine?.disConnect(webSocket, socketWrapperListener)
        }
        return this
    }

    /**
     * 发送文本数据
     */
    fun send(text: String?) {
        if (text.isNullOrBlank()) return
        val request = RequestFactory.createStringRequest()
        request.setRequestData(text)
        sendRequest(request)
    }

    /**
     * 发送 byte[] 数据
     */
    fun send(bytes: ByteArray?) {
        if (bytes == null || bytes.isEmpty()) return
        val request: Request<ByteArray> = RequestFactory.createByteArrayRequest()
        request.setRequestData(bytes)
        sendRequest(request)
    }

    /**
     * 发送 ByteBuffer 数据
     */
    fun send(byteBuffer: ByteBuffer?) {
        if (byteBuffer == null) return
        val request: Request<ByteBuffer> = RequestFactory.createByteBufferRequest()
        request.setRequestData(byteBuffer)
        sendRequest(request)
    }

    /**
     * 发送 Ping
     */
    fun sendPing() = sendRequest(RequestFactory.createPingRequest())

    /**
     * 发送 Pong
     */
    fun sendPong() = sendRequest(RequestFactory.createPongRequest())

    /**
     * 发送 Pong
     */
    fun sendPong(pingFrame: PingFrame?) {
        if (pingFrame == null) return
        val request: Request<PingFrame> = RequestFactory.createPongRequest()
        request.setRequestData(pingFrame)
        sendRequest(request)
    }

    /**
     * 发送 [Framedata]
     */
    fun sendFrame(framedata: Framedata?) {
        if (framedata == null) return
        val request: Request<Framedata> = RequestFactory.createFrameDataRequest()
        request.setRequestData(framedata)
        sendRequest(request)
    }

    /**
     * 发送 [Framedata] 集合
     */
    fun sendFrame(frameData: Collection<Framedata>?) {
        if (frameData == null) return
        val request = RequestFactory.createCollectionFrameRequest()
        request.setRequestData(frameData)
        sendRequest(request)
    }

    /**
     * 添加一个监听器，使用完成之后需要调用
     * [.removeListener] 方法移除监听器
     */
    fun addListener(listener: SocketListener): WebSocketManager {
        delivery?.addListener(listener)
        return this
    }

    /**
     * 添加一个监听器，使用完成之后需要调用
     * [.removeListener] 方法移除监听器
     */
    fun addListener(listener: (message: String, data: Any?) -> Unit) {
        addListener(object : StringSocketListener() {
            override fun <T> onMessage(message: String, data: T?) {
                listener(message, data)
            }
        })
    }

    /**
     * 移除一个监听器
     */
    fun removeListener(listener: SocketListener): WebSocketManager {
        delivery?.removeListener(listener)
        return this
    }

    /**
     * 彻底销毁该链接，销毁后改链接完全失效，
     * 请勿使用其他方法。
     */
    fun destroy() {
        destroyed = true
        if (webSocket != null) {
            webSocketEngine?.destroyWebSocket(webSocket)
        }
        delivery?.clear()
        if (reconnectManager?.reconnecting() == true) {
            reconnectManager?.stopReconnect()
        }
        delivery = null
        webSocket = null
        webSocketEngine = null
        reconnectManager = null
    }

    /**
     * 重新链接一次,
     * for [ReconnectManager]
     */
    fun reconnectOnce() {
        if (destroyed) {
            LogUtil.e("This WebSocketManager is destroyed!")
            return
        }
        if (webSocket?.getConnectState() == 0) {
            webSocketEngine?.connect(webSocket, socketWrapperListener)
        } else {
            reconnectManager?.onConnected()
            LogUtil.e("WebSocket 已链接，请勿重试。")
        }
    }

    /**
     * 发送数据
     */
    private fun sendRequest(request: Request<*>) {
        if (destroyed) {
            LogUtil.e("This WebSocketManager is destroyed!")
            return
        }
        webSocketEngine?.sendRequest(webSocket, request, socketWrapperListener)
    }

    /**
     * 获取默认的重连器
     */
    private fun getDefaultReconnectManager(): DefaultReconnectManager {
        return DefaultReconnectManager(this, object : ReconnectManager.OnConnectListener {
            override fun onConnected() {
                LogUtil.e("重连成功 :${isConnected()}")
            }

            override fun onDisconnect() {
                LogUtil.e("重连失败")
                setting?.responseProcessDispatcher?.onDisconnect(delivery)
            }
        })
    }

    /**
     * 获取监听器
     */
    private fun getSocketWrapperListener(): SocketWrapperListener {
        return object : SocketWrapperListener {
            override fun onConnected() {
                reconnectManager?.onConnected()
                setting?.responseProcessDispatcher?.onConnected(delivery)
            }

            override fun onConnectFailed(e: Throwable?) {
                //if reconnecting,interrupt this event for ReconnectManager.
                if (reconnectManager?.reconnecting() == true) {
                    reconnectManager?.onConnectError(e)
                }
                setting?.responseProcessDispatcher?.onConnectFailed(e, delivery)
            }

            override fun onDisconnect() {
                setting?.responseProcessDispatcher?.onDisconnect(delivery)
                if (reconnectManager?.reconnecting() == true) {
                    if (disconnect) {
                        setting?.responseProcessDispatcher?.onDisconnect(delivery)
                    } else {
                        reconnectManager?.onConnectError(null)
                    }
                } else {
                    if (!disconnect) {
                        if (reconnectManager == null) {
                            reconnectManager = getDefaultReconnectManager()
                        }
                        reconnectManager?.onConnectError(null)
                        reconnectManager?.startReconnect()
                    }
                }
            }

            override fun onSendDataError(request: Request<*>?, type: Int, tr: Throwable?) {
                val errorResponse = ResponseFactory.createErrorResponse()
                errorResponse.init(request, type, tr)
                setting?.apply {
                    if (processDataOnBackground) {
                        responseProcessEngine?.onSendDataError(errorResponse, responseProcessDispatcher, delivery)
                    } else {
                        responseProcessDispatcher?.onSendDataError(errorResponse, delivery)
                    }
                }
                if (!disconnect && type == ErrorResponse.ERROR_NO_CONNECT) {
                    LogUtil.e("数据发送失败，网络未链接，开始重连。。。 >>${tr?.message}")
                    reconnect()
                }
            }

            override fun onMessage(message: Response<*>?) {
                setting?.apply {
                    if (processDataOnBackground) {
                        responseProcessEngine?.onMessageReceive(message, responseProcessDispatcher, delivery)
                    } else {
                        message?.onResponse(responseProcessDispatcher, delivery)
                    }
                }
            }
        }
    }
}